﻿namespace SpikingAnalyticsLib.ResponseFingerPrint

open SpikingAnalyticsLib
open SpikingAnalyticsLib.GeneralUtilities

//=================================================================================================

/// A set of response frames for a Response Fingerprint
type ResponseFingerPrintFrameSet (frames:ResponseFingerPrintFrame []) =

    // For each frame produce a score for each pattern using whole probability distributions
    let ScoreFramesWholeDistributions patternName windowMapSet patternFrequencyMap =

        frames
        |> Seq.map (fun (frame) ->
                frame.GetScore(patternName, windowMapSet, patternFrequencyMap)
            )
        |> Seq.toArray

//---------------------------------------------------------------------------------------------------------
// Constructors

    new(selectedNeurons, totalNeurons, frameSize, runSeconds, firingData:seq<int * int * SpikingNeuronLib.EventLabel>) =
        let frameSet = new FrameSet(selectedNeurons, totalNeurons, frameSize, runSeconds, firingData)
        let frames =
            frameSet.ToEnumerable()
            |> Seq.map (fun frame -> new ResponseFingerPrintFrame(frame.OffsetMap))
            |> Seq.toArray
        new ResponseFingerPrintFrameSet(frames)

//---------------------------------------------------------------------------------------------------------
// Properties

    /// Get the number of Frame instances
    member this.Length = frames.Length

    /// Retrieve a specific Frame instance
    member this.Item
        with get(index) = frames.[index]
        //and set index value = frames.[index] <- value

//---------------------------------------------------------------------------------------------------------
// Methods

    /// Return a sequence of Frame instances
    member this.ToEnumerable() = Seq.init frames.Length (fun index -> frames.[index])

    member this.GetScores(patternName, windowMapSet, patternFrequencyMap) =
        let scores = ScoreFramesWholeDistributions patternName windowMapSet patternFrequencyMap
        new FrameScoreSet(scores)

    // Get a list of the firing events that occurred in each frame
    member this.GetFiringEventsByFrame(combinedMap) =
        frames
        |> Array.map (fun frame ->
                frame.GetWindowFiringEvents(combinedMap)
            )

    // Get a list of the spike times for each window, accumulated across frames
    member this.GetAccumulatedWindowSpikeTimes(combinedMap) =
        // retrieve the spike times for each frame and merge across all frames
        frames
        |> Seq.map (fun frame ->
                frame.GetWindowSpikeTimes(combinedMap)
            )
        |> Seq.fold (fun (state:Map<(string * int), int list>) windowSpikeTimesByFrame ->
                let mergedState =
                    windowSpikeTimesByFrame
                    |> Map.map (fun window spikeTimes ->
                            if state.ContainsKey window then
                                List.append spikeTimes state.[window]
                            else
                                spikeTimes
                        )
                mergedState
            ) Map.empty

//=================================================================================================

/// The set of scores for all frames
and FrameScoreSet (scores:FrameScore []) =

    // Given a list of scores, generate the match results (a column of data in the final result table)
    // Each item in the result list corresponds to:
    //  matched: the frame passed the match test
    //  score: the score for the frame (possibly with additional markup therefore as a string)
    //  coincidentSpikes: the number of coincident window spikes in the frame
    let GetScoreData testPattern (selectedPatternBySecond:Map<int, _>) =
        let scoreData =
            scores
            |> Seq.mapi (fun index score ->
                    let presentedPattern = selectedPatternBySecond.[index]

                    score.GetScoreDescription(testPattern, presentedPattern)
                )
            |> Seq.toList

        scoreData

//---------------------------------------------------------------------------------------------------------
// Properties

    member this.Length = scores.Length

    member this.Scores = scores

//---------------------------------------------------------------------------------------------------------
// Methods

    member this.GetScoreDescriptions(testPattern, selectedPatternBySecond) =
        GetScoreData testPattern selectedPatternBySecond

//=================================================================================================
